package com.smssalama.gui;

import java.util.Vector;

import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;

import com.smssalama.AlertManager;
import com.smssalama.SMSSalama;
import com.smssalama.SMSSalamaSession;
import com.smssalama.TransportBridge;
import com.smssalama.i18n.CommandLabels;
import com.smssalama.i18n.Labels;
import com.smssalama.i18n.ScreenTitles;
import com.smssalama.storage.contact.Contact;
import com.smssalama.storage.contact.ContactFilter;
import com.smssalama.storage.framework.ObjectListener;
import com.smssalama.storage.framework.ObjectStore;
import com.smssalama.storage.framework.ObjectStore.ObjectEnumeration;
import com.smssalama.storage.sms.SMSMessage;
import com.smssalama.storage.sms.SMSMessageFilter;
import com.smssalama.storage.sms.SMSMessageIn;

/**
 * Displays a list of messages. 
 * 
 * <p>
 * 	It can be supplied with a filter for the messages as well.
 * 	This can be useful when creating folders for certain types of messages only.
 *  You can even let a user add add his/her own folders with a custom filter
 * </p>
 * 
 * <p>If a filter is nor provided, all messages are displayed</p>
 * @author Arnold P. Minde
 *
 */
public class Messages extends List implements CommandListener {
	private Labels LABELS = Labels.labels();
	private CommandLabels CMD_LABELS = CommandLabels.cmdLabels();

	private class ObjectListenerImpl implements ObjectListener {
		ObjectStore messageStore;

		ObjectListenerImpl(ObjectStore messageStore) {
			this.messageStore = messageStore;
		}

		public void objectAdded(ObjectStore store, int recordId) {
			initRecords(Messages.this.filter);
		}

		public void objectChanged(ObjectStore store, int recordId) {
			// Reload the change object Object
			reloadMessage(recordId);
		}

		public void objectDeleted(ObjectStore store, int recordId) {
			int index = Messages.this.messageIds.indexOf(new Integer(recordId));
			removeMessage(index);
		}
	}

	public final Command viewCmd = new Command(CMD_LABELS.view(), Command.ITEM, 1);
	public final Command backCmd = new Command(CMD_LABELS.back(), Command.BACK, 1);
	public final Command exitCmd = new Command(CMD_LABELS.exit(), Command.BACK, 3);
	public final Command newCmd = new Command(CMD_LABELS.NEW(), Command.BACK, 2);
	public final Command deleteCmd = new Command(CMD_LABELS.delete(), Command.ITEM,
			2);

	// private int NEW_MESSAGE_ITEM_INDEX = 0;
	// private int INBOX_ITEM_INDEX = 0;

	private final Vector/* <Integer, > */messageIds = new Vector();
	private final Displayable back;
	private final SMSMessageFilter filter;
	private final ObjectListenerImpl objectListener;

	public Messages(Displayable back) {
		this(back, "", SMSMessageFilter.PASSTHROUGH);
		this.setTitle(LABELS.messages());
	}

	public Messages(Displayable back, String caption, SMSMessageFilter filter) {
		super(caption, Choice.IMPLICIT);
		this.back = back;
		this.filter = filter;
		ObjectListenerImpl tmp = null;
		try {
			tmp = new ObjectListenerImpl(SMSSalamaSession.getSession()
					.getUserSettings().getMessagesStore());
		} catch (Exception e) {
			e.printStackTrace();
			this.append("Error: " + e.getMessage(), null);
		}
		this.objectListener = tmp;
		try {
			initRecords(filter);
			this.setSelectCommand(this.viewCmd);
			this.addCommand(this.backCmd);
			this.addCommand(this.exitCmd);
			this.addCommand(this.newCmd);
			this.addCommand(this.deleteCmd);
			this.setCommandListener(this);
		} catch (Exception e) {
			e.printStackTrace();
			this.append("Error: " + e.getMessage(), null);
			this.addCommand(new Command(CMD_LABELS.back(),
					Command.BACK, 1));
			// this.append("Error: "+e.getMessage());
		}
	}

	public void removeMessage(int index) {
		if (index >= 0 && index < this.messageIds.size()) {
			this.delete(index);
			this.messageIds.removeElementAt(index);
		}
	}

	private int getIdAtIndex(int index) {
		// if(index<0 || index>=messageIds.size()) return -1;
		Integer id = (Integer) this.messageIds.elementAt(index);
		if (id == null) {
			return 0;
		} else {
			return id.intValue();
		}
	}

	private int getSelectedId() {
		int index = this.getSelectedIndex();
		if (index >= 0 && index < this.messageIds.size()) {
			return getIdAtIndex(index);
		} else {
			return -1;
		}
	}

	private void selectId(int messageId) {
		int index = this.messageIds.indexOf(new Integer(messageId));
		if (index >= 0) {
			this.setSelectedIndex(index, true);
		}
	}

	/**
	 * Searches for contacts using the given filter and display the list.
	 * 
	 * After search, it also attempts to restore the selected item. It first
	 * tries to restore by id of the message. if the message with the id is not
	 * on the list, it tries to restore selection to the same index as it was
	 * before the search. If this index is past the end of the list, it tries
	 * the one before it.
	 * 
	 * @param filter
	 * @throws Exception
	 */
	public void initRecords(SMSMessageFilter filter){
		try {
			int selectedId = getSelectedId();
			int selectedIndex = getSelectedIndex();
			ObjectStore messageStore = SMSSalamaSession.getSession()
					.getUserSettings().getMessagesStore();
			messageStore.removeObjectListener(this.objectListener);
			ObjectEnumeration enumeration = messageStore.search(filter, null);
			try {
				int i = 0;
				while (enumeration.hasNextObject()) {
					SMSMessage message = (SMSMessage) enumeration.nextObject();
					String displayName = SMSSalamaSession.getSession()
							.getUserSettings().getDisplayNameCache()
							.getPhoneNumberDisplayName(message.getPhoneNumber());
					if (i < this.size()) {
						this.set(i, displayName, null);
						this.messageIds.setElementAt(new Integer(message.getRecordId()), i);
					} else {
						this.append(displayName, null);
						this.messageIds.addElement(new Integer(message.getRecordId()));
					}
					i++;
				}
				while (i > this.size()) {
					this.delete(this.size() - 1);
				}
				while (i > this.messageIds.size() && this.messageIds.size() > 0) {
					this.messageIds.removeElementAt(this.messageIds.size() - 1);
				}
			} finally {
				enumeration.close();
			}
			selectId(selectedId);
			if (this.getSelectedId() != selectedId) {
				while (selectedIndex >= this.messageIds.size()) {
					selectedIndex--;
				}
				if (selectedIndex >= 0) {
					this.setSelectedIndex(selectedIndex, true);
				}
			}
			messageStore.addObjectListener(this.objectListener);
		} catch (Exception e) {
			e.printStackTrace();
			this.append("Error: "+e.getMessage(), null);
		}
	}
	
	private void reloadMessage(int recordId){
		try {
			int index = messageIds.indexOf(new Integer(recordId));
			SMSMessage message = (SMSMessage) SMSSalamaSession.getSession().getUserSettings().getMessagesStore().getStorable(recordId);
			String displayName = SMSSalamaSession.getSession()
				.getUserSettings().getDisplayNameCache()
				.getPhoneNumberDisplayName(message.getPhoneNumber());
			this.set(index, displayName, null);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void viewMessage() throws Exception {
		int messageId = getSelectedId();
		if (messageId <= 0) {
			return;
		}
		SMSMessage message = (SMSMessage) SMSSalamaSession.getSession()
				.getUserSettings().getMessagesStore().getStorable(messageId);
		final SMSMessageViewer view;
		view = new SMSMessageViewer(message);
		view.addCommand(this.backCmd);
		view.addCommand(this.exitCmd);
		view.setCommandListener(new CommandListener() {
			public void commandAction(Command command, Displayable displayable) {
				if (command.getCommandType() == Command.BACK
						|| command.getCommandType() == Command.CANCEL) {
					AlertManager.showDisplayable(Messages.this);
				} else if (command.getCommandType() == Command.EXIT) {
					SMSSalama.quitApp();
				} else {
					view.commandAction(command, displayable);
				}
			}
		});
		if (message instanceof SMSMessageIn
				&& ((SMSMessageIn) message).isEncrypted()) {
			AlertManager.showItem(view.password);
		} else {
			AlertManager.showDisplayable(view);
		}
	}

	private void newMessage() {
		final TextBox textBox = new TextBox(ScreenTitles.screenTitles().messageEditorNew(), "", 800,
				TextField.ANY);

		textBox.addCommand(new Command(CMD_LABELS.back(),
				Command.BACK, 1));
		textBox.addCommand(new Command(CMD_LABELS.send(),
				Command.OK, 1));
		textBox.setCommandListener(new CommandListener() {
			public void commandAction(Command command, Displayable displayable) {
				if (command.getCommandType() == Command.BACK
						|| command.getCommandType() == Command.CANCEL) {
					AlertManager.showDisplayable(Messages.this);
				} else if (command.getCommandType() == Command.OK) {
					// SMSSalama.showDisplayable(MessageConstants.this);
					selectContact(textBox);
				}
			}

		});
		AlertManager.showDisplayable(textBox);
	}

	private void selectContact(final TextBox textBox) {
		final SelectContact selectContact = new SelectContact("Select Contact");
		selectContact.addCommand(new Command(CMD_LABELS.back(),
				Command.BACK, 1));
		selectContact.setCommandListener(new CommandListener() {
			public void commandAction(Command command, Displayable displayable) {
				if (command.getCommandType() == Command.BACK) {
					AlertManager.showDisplayable(textBox);
				} else if (command.getCommandType() == Command.OK) {
					final String number = selectContact.getSelectedNumber();
					final String text = textBox.getString();
					String sharedKey = null;
					try {
						sharedKey = getSharedKey(number);
						if (sharedKey != null) {
							sendMessage(number, sharedKey, textBox.getString());
						} else {
							// Prompt for shared key
							Form sharedKeyForm = new Form("Enter Password");
							final TextField field = new TextField(
									"Password for the message", "", 100,
									TextField.PASSWORD);
							sharedKeyForm.append(field);
							AlertManager.showDisplayable(sharedKeyForm);
							sharedKeyForm.addCommand(new Command("OK",
									Command.OK, 1));
							sharedKeyForm.addCommand(new Command("Back",
									Command.BACK, 2));
							sharedKeyForm
									.setCommandListener(new CommandListener() {
										public void commandAction(
												Command command,
												Displayable displayble) {
											if (command.getCommandType() == Command.BACK) {
												AlertManager
														.showDisplayable(selectContact);
											} else if (command.getCommandType() == Command.OK) {
												sendMessage(number, field
														.getString(), text);
											}
										}
									});
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		});
		AlertManager.showDisplayable(selectContact);
	}

	protected void sendMessage(String number, String sharedKey, String text) {
		try {
			TransportBridge.getBridge().sendSMS(number, sharedKey, text);
			AlertManager.showDisplayable(this);
		} catch (Exception e) {
			e.printStackTrace();
			AlertManager.showError(this, e);
		}
		/*
		 * SMSMessageOut message = new SMSMessageOut(number, new
		 * Date().getTime(), text); message.setStatus(SMSMessageOut.PENDING);
		 * try {
		 * SMSSalama.getInstance().getSettings().getMessagesStore().persistStorable(message);
		 * SMSSalama.showDisplayable(this); } catch (Exception e) {
		 * e.printStackTrace(); SMSSalama.showError(this, e); }
		 */
	}

	protected String getSharedKey(String number) throws Exception {
		ObjectEnumeration enumeration = SMSSalamaSession.getSession()
				.getUserSettings().getContactsStore().search(
						new ContactFilter(null, number), null);
		while (enumeration.hasNextObject()) {
			Contact contact = (Contact) enumeration.nextObject();
			if (contact.getSharedKey() != null) {
				return contact.getSharedKey();
			}
		}
		return null;
	}

	public void commandAction(Command command, Displayable displayable) {
		if (command == this.viewCmd) {
			try {
				viewMessage();
			} catch (Exception e) {
				e.printStackTrace();
				AlertManager.showError(this, e);
			}
		} else if (command == this.backCmd) {
			if (this.back != null) {
				AlertManager.showDisplayable(this.back);
			}
		} else if (command == this.exitCmd) {
			SMSSalama.quitApp();
		} else if (command == this.newCmd) {
			newMessage();
		} else if (command == this.deleteCmd) {
			int id = getSelectedId();
			if (id <= 0) {
				return;
			}
			ObjectStore messageStore;
			try {
				messageStore = SMSSalamaSession.getSession().getUserSettings()
						.getMessagesStore();
				messageStore.deleteStorable(id);
			} catch (Exception e) {
				e.printStackTrace();
				AlertManager.showError(this, e);
			}
		}
	}
}